//
//  SFMovableGroupAndItemTableViewCell.m
//  DMP
//
//  Created by 支晓辉 on 16/4/8.
//  Copyright © 2016年 Lxit. All rights reserved.
//

#import "SFMovableGroupAndItemTableViewCell.h"

#import "SFMovableTableViewController.h"

#import <mach/mach_time.h>

/** 动画持续的时间 */
#define ANIMATE_DURATION 0.3f
#define kSepLineHeight [self.class sepLineHeight]

@interface SFMovableGroupAndItemTableViewCell ()
/** 存放内容的视图 */
@property (weak, nonatomic) IBOutlet UIView *bodyView;

/** 每一行的视图集合 */
@property (strong, nonatomic) NSMutableArray<MOVABLE_VIEW*> *rowViews;
/** 可移动子视图相关的约束 */
@property (strong, nonatomic) NSMutableArray<NSLayoutConstraint*> *bodyConstraints;

@end

@implementation SFMovableGroupAndItemTableViewCell
{
    BOOL isInitGesture;     //!< 是否已经初始化手势
    BOOL isAnimate;         //!< 是否正在动画中
}
@synthesize groupData = _groupData;

- (instancetype)init {
    if (self = [super init]) {
        [self _initSubView];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self _initSubView];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        [self _initSubView];
    }
    return self;
}

#pragma mark - Override

- (void)initLongPressGesture:(UILongPressGestureRecognizer *)gesture {
    if (isInitGesture) {
        return;
    }
    if (!self.titleSpanView) {
        return;
    }
    isInitGesture = YES;
    gesture.minimumPressDuration = 0.5f;
    [self.titleSpanView addGestureRecognizer:gesture];
    self.titleSpanView.userInteractionEnabled = YES;
}

#pragma mark - Public methods

/**
 *  更新每行的子视图
 */
- (void)updateChildren {
    if (!self.groupData.expanded) {
        for (NSInteger i = 0, count = self.rowViews.count; i < count; i++) {
            MOVABLE_VIEW *v = self.rowViews.lastObject;
            [self.rowViews removeLastObject];
            [v removeFromSuperview];
            if (self.dequeueDelegate) {
                [self.dequeueDelegate unuseWithMovableView:v];
            }
        }
        [self removeAllConstraintsForMovableView];
        return;
    }
    
    if (self.rowViews.count > self.groupData.childData.count) {
        NSInteger surplusViewCount = self.rowViews.count - self.groupData.childData.count;
        
        for (NSInteger i = 0; i < self.groupData.childData.count; i++) {
            SFGroupItem *item = self.groupData.childData[i];
            MOVABLE_VIEW *v   = self.rowViews[i];
            v.parent          = self.groupData;
            v.item            = item;
        }
        for (NSInteger i = 0; i < surplusViewCount; i++) {
            MOVABLE_VIEW *v = self.rowViews.lastObject;
            [self.rowViews removeLastObject];
            [v removeFromSuperview];
            if (self.dequeueDelegate) {
                [self.dequeueDelegate unuseWithMovableView:v];
            }
        }
        [self updateConstraintsForMovableView];
    } else if (self.rowViews.count < self.groupData.childData.count) {
        NSInteger rowViewsCount = self.rowViews.count;
        
        for (NSInteger i = 0; i < self.groupData.childData.count; i++) {
            if (i >= rowViewsCount) {
                MOVABLE_VIEW *v = [self createMovableViewWithData:self.groupData.childData[i]
                                                            index:i];
                [self.bodyView addSubview:v];
                [self.rowViews addObject:v];
            } else {
                SFGroupItem *item = self.groupData.childData[i];
                MOVABLE_VIEW *v   = self.rowViews[i];
                v.parent          = self.groupData;
                v.item            = item;
            }
        }
        [self updateConstraintsForMovableView];
    } else {
        for (NSInteger  i = 0; i < self.groupData.childData.count; i++) {
            SFGroupItem *item = self.groupData.childData[i];
            MOVABLE_VIEW *v   = self.rowViews[i];
            v.parent          = self.groupData;
            v.item            = item;
        }
    }
}

- (void)reloadRowAtIndex:(NSUInteger)index {
    if (index < self.rowViews.count) {
        SFMovableView *v = self.rowViews[index];
        //强制调用重新配置视图显示的数据
        v.item = v.item;
    }
}

#pragma mark Get movable view

- (MOVABLE_VIEW *)createMovableViewAtIndex:(NSInteger)index item:(SFGroupItem *)item {
    if (self.dequeueDelegate && self.itemViewReuseIdentifier) {
        return [self.dequeueDelegate
                dequeueWithMovableViewIdentifier:self.itemViewReuseIdentifier];
    }
    return [[MOVABLE_VIEW alloc] init];
}

#pragma mark Movable control

- (void)insertDataAt:(NSInteger)index data:(SFGroupItem*)item animated:(BOOL)animated  complete:(void (^)())completeBlock {
    
    MOVABLE_VIEW *v = [self createMovableViewWithData:item index:index];
    v.item = item;
    
    if (index >= self.groupData.childData.count) {
        [self.groupData.childData addObject:item];
        [self.rowViews addObject:v];
    } else {
        [self.groupData.childData insertObject:item  atIndex:index];
        [self.rowViews insertObject:v atIndex:index];
    }
    
    if (animated) { //执行动画显示
        isAnimate = YES;
        v.alpha       = 0.f;
    }
    [self.bodyView addSubview:v];
    [UIView animateWithDuration:animated ? ANIMATE_DURATION:0.f animations:^{
        v.alpha = 1.f;
        [self updateConstraintsForMovableView];
        [self.bodyView layoutIfNeeded];
    } completion:^(BOOL finished) {
        isAnimate = NO;
        if (completeBlock) {
            completeBlock();
        }
    }];
}

- (void)moveDataFrom:(NSInteger)srcIndex to:(NSInteger)destIndex animated:(BOOL)animated complete:(void (^)())completeBlock {
    if (srcIndex < 0 || srcIndex >= self.groupData.childData.count ||
        destIndex < 0 || destIndex >= self.groupData.childData.count ||
        srcIndex == destIndex) {
        return;
    }
    
    SFGroupItem *srcItem = self.groupData.childData[srcIndex];
    MOVABLE_VIEW *srcMovableView = self.rowViews[srcIndex];
    
    [self.rowViews removeObjectAtIndex:srcIndex];
    
    [self.rowViews insertObject:srcMovableView atIndex:destIndex];
    
    isAnimate = animated;
    [self.groupData.childData removeObjectAtIndex:srcIndex];
    [self.groupData.childData insertObject:srcItem atIndex:destIndex];
    [UIView animateWithDuration:animated ? ANIMATE_DURATION:0.f animations:^{
        [self updateConstraintsForMovableView];
        
        [self.bodyView layoutIfNeeded];
    } completion:^(BOOL finished) {
        isAnimate = NO;
        if (completeBlock) {
            completeBlock();
        }
    }];
}

- (void)deleteDataAt:(NSInteger)index animated:(BOOL)animated {
    if (index < 0 || index >= self.groupData.childData.count) {
        return;
    }
    
    MOVABLE_VIEW *movableView = self.rowViews[index];
    UIView *prevView = index - 1 >= 0 ? self.rowViews[index - 1]:nil;
    
    //删除对应的数据
    [self.rowViews removeObjectAtIndex:index];
    
    isAnimate = animated;
    
    [self.groupData.childData removeObjectAtIndex:index];
    
    __weak typeof(self) wself = self;
    [UIView animateWithDuration:animated ? ANIMATE_DURATION:0.f animations:^{
        movableView.alpha = 0.f;
        
        //这里是删除的时候保持在原来的位置，看起来删除动画好看使用的
        if (animated) {
            if (prevView) {
                [self.bodyView addConstraints:[NSLayoutConstraint
                                               constraintsWithVisualFormat:@"V:[prevView]-0-[movableView]"
                                               options:0
                                               metrics:nil
                                               views:NSDictionaryOfVariableBindings(movableView, prevView)]];
            }else {
                [self.bodyView addConstraints:[NSLayoutConstraint
                                               constraintsWithVisualFormat:@"V:|-0-[movableView]"
                                               options:0
                                               metrics:nil
                                               views:NSDictionaryOfVariableBindings(movableView)]];
            }
        }
        
        [wself updateConstraintsForMovableView];
        [wself.bodyView layoutIfNeeded];
    } completion:^(BOOL finished) {
        [movableView removeFromSuperview];
        isAnimate = NO;
        if (wself.dequeueDelegate) {
            [wself.dequeueDelegate unuseWithMovableView:movableView];
        }
    }];
}

- (void)updateChildrenLayoutWithAnimation:(BOOL)animated {
    [self updateChildrenLayoutWithAnimation:animated otherAnimation:nil complete:nil];
}

- (void)updateChildrenLayoutWithAnimation:(BOOL)animated otherAnimation:(void (^)())otherAnimation complete:(void (^)(BOOL finished))completeBlock {
    void (^layoutBlock)() = ^{
        for (NSInteger i = 0; i < self.rowViews.count; i++) {
            if (i >= self.rowViews.count) {
                return;
            }
            MOVABLE_VIEW *movableView = self.rowViews[i];
            CGRect r = movableView.frame;
            r.origin.y = i * ([movableView.class rowHeight] + kSepLineHeight);
            movableView.frame = r;
        }
        if (otherAnimation) {
            otherAnimation();
        }
    };
    if (animated) {
        [UIView animateWithDuration:0.5f animations:layoutBlock completion:completeBlock];
    } else {
        layoutBlock();
        completeBlock(YES);
    }
}

#pragma mark Check intersection rect

- (NSInteger)intersectMovableViewRectWith:(CGRect)screenRect filterItem:(SFGroupItem*)item {
    for (NSInteger i = self.rowViews.count - 1; i >= 0; i--) {
        MOVABLE_VIEW *v = self.rowViews[i];
        if (v.item == item) {
            continue;
        }
        CGRect targetRect = [self.bodyView convertRect:v.frame toView:[UIApplication sharedApplication].keyWindow];
        CGRect topRect = CGRectMake(
                                    CGRectGetMinX(targetRect),
                                    CGRectGetMinY(targetRect),
                                    CGRectGetWidth(targetRect),
                                    CGRectGetHeight(targetRect) / 2.f
                                    );
        CGRect bottomRect = CGRectMake(
                                       CGRectGetMinX(targetRect),
                                       CGRectGetMidY(targetRect),
                                       CGRectGetWidth(targetRect),
                                       CGRectGetHeight(targetRect) / 2.f
                                       );
        /** 跟上半边相交且用户拖动的矩形的Y坐标比当前项的Y坐标大 */
        BOOL topIntersect = CGRectIntersectsRect(screenRect, topRect) &&
        CGRectGetMinY(screenRect) > CGRectGetMinY(topRect);
        /** 跟上半边相交且用户拖动的矩形的Y坐标比当前项的Y坐标小 */
        BOOL topIntersectYNotGreaterThanTarget = CGRectIntersectsRect(screenRect, topRect) &&
        CGRectGetMinY(screenRect) <= CGRectGetMinY(topRect);
        /** 跟下半边相交且用户拖动的矩形的Y坐标比当前项的Y坐标大 */
        BOOL bottomIntersect = CGRectIntersectsRect(screenRect, bottomRect) &&
        CGRectGetMinY(screenRect) > CGRectGetMinY(bottomRect);
        
        if (topIntersect || topIntersectYNotGreaterThanTarget || bottomIntersect) {
            //以下两项判断主要用于跨组移动的时候
            if ((topIntersectYNotGreaterThanTarget && i != 0) ||        //如果与上半边相交但是不是组的第一个项
                (bottomIntersect && i != self.rowViews.count - 1) ) {   //如果与下半边相交但是不是组的最后一项
                continue;
            }
            NSInteger resIndex = -1;
            resIndex = i;
            return resIndex;
        }
    }
    return -1;
}

#pragma mark View control

#pragma mark Get row view

- (MOVABLE_VIEW *)movableViewAtIndex:(NSInteger)index {
    if (index < self.rowViews.count) {
        return self.rowViews[index];
    }
    return nil;
}

- (MOVABLE_VIEW *)getMovableViewByPoint:(CGPoint)pt {
    for (MOVABLE_VIEW *v in self.rowViews) {
        CGRect screenRect = [self.bodyView convertRect:v.frame toView:[UIApplication sharedApplication].keyWindow];
        if (CGRectContainsPoint(screenRect, pt)) {
            return v;
        }
    }
    return nil;
}


#pragma mark Static methods

+ (CGFloat)calcRowHeightWithGroup:(SFGroup*)group {
    CGFloat res = group.childData.count;
    CGFloat height = res * [MOVABLE_VIEW rowHeight] + res * kSepLineHeight + 46.f;
    //结尾的45为标题栏的高度35再加上底部外边距的10
    return height;
}

+ (CGFloat)sepLineHeight {
    return 1.f;
}

#pragma mark - Private methods

/**
 *  初始化子视图
 */
- (void)_initSubView {
    self.selectionStyle = UITableViewCellSelectionStyleNone;
}


/**
 *  更新所有子视图的约束
 */
- (void)updateConstraintsForMovableView {
    [self removeAllConstraintsForMovableView];
    
    for (NSInteger i = self.groupData.childData.count - 1; i >= 0; i--) {
        if (i >= self.rowViews.count) {
            continue;
        }
        MOVABLE_VIEW *movableView = self.rowViews[i];
        if (movableView.translatesAutoresizingMaskIntoConstraints) {
            //不给新加入的movableView加入隐含的x,y,w,h的autolayout
            movableView.translatesAutoresizingMaskIntoConstraints = NO;
        }
        
        NSArray<NSLayoutConstraint*> *constraint = nil;
        if (i == 0) {
            constraint = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[movableView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(movableView)];
            [self.bodyConstraints addObjectsFromArray:constraint];
            [self.bodyView addConstraints:constraint];
            
            constraint = [NSLayoutConstraint
                          constraintsWithVisualFormat:@"V:|-0-[movableView(height)]"
                          options:0
                          metrics:@{
                                    @"height":@([movableView.class rowHeight])
                                    }
                          views:NSDictionaryOfVariableBindings(movableView)];
            [self.bodyConstraints addObjectsFromArray:constraint];
            [self.bodyView addConstraints:constraint];
        } else {
            UIView *prevView = self.rowViews[i - 1];
            constraint = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[movableView]-0-|"
                                                                 options:0
                                                                 metrics:nil
                                                                   views:NSDictionaryOfVariableBindings(movableView)];
            [self.bodyConstraints addObjectsFromArray:constraint];
            [self.bodyView addConstraints:constraint];
            
            constraint = [NSLayoutConstraint
                          constraintsWithVisualFormat:@"V:[prevView]-0-[movableView(height)]"
                          options:0
                          metrics:@{
                                    @"height":@([movableView.class rowHeight])
                                    }
                          views:NSDictionaryOfVariableBindings(
                                                               movableView,
                                                               prevView
                                                               )
                          ];
            [self.bodyConstraints addObjectsFromArray:constraint];
            [self.bodyView addConstraints:constraint];
        }
    }
}

/**
 *  移除所有子视图的约束
 */
- (void)removeAllConstraintsForMovableView {
    for (MOVABLE_VIEW *v in self.rowViews) {
        [v removeConstraints:self.bodyConstraints];
    }
    [self.bodyView removeConstraints:self.bodyConstraints];
    [self.bodyConstraints removeAllObjects];
}

/**
 *  创建可移动的视图
 *
 *  @param i     对应的数据模型
 *  @param index 视图哦所在的索引
 *
 */
- (MOVABLE_VIEW*)createMovableViewWithData:(SFGroupItem*)i index:(NSInteger)index {
    MOVABLE_VIEW *v = nil;
    v = (MOVABLE_VIEW*)[self createMovableViewAtIndex:index item:i];
    v.parent = self.groupData;
    if (i) {
        v.item = i;
    }
    
    [self.bodyView addSubview:v];
    return v;
}

/**
 *  生成分割线视图
 */
- (UIView*)createSepLineViewWithIndex:(NSInteger)index {
    UIView *sepLineView = [[UIView alloc] init];
    if (self.notUseSepLine) {
        sepLineView.hidden = YES;
    }
    sepLineView.backgroundColor = [UIColor lightGrayColor];
    
    sepLineView.frame = CGRectMake(
                                   0,
                                   index * [MOVABLE_VIEW rowHeight] + [MOVABLE_VIEW rowHeight] + kSepLineHeight * index,
                                   CGRectGetWidth(self.bodyView.frame),
                                   kSepLineHeight
                                   );
    
    return sepLineView;
}

#pragma mark KVO

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"expanded"]) {
        dispatch_async_in_main(^{
            if (self.groupData.expanded && self.bodyConstraints.count <= 0) {
                [self updateChildren];
            }
        });
    }
}

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    
    //禁用掉SWTableViewCell的手势
    if ([NSStringFromClass(gestureRecognizer.view.class) hasPrefix:@"SW"]) {
        return NO;
    }
    
    if ([super respondsToSelector:@selector(gestureRecognizer:shouldReceiveTouch:)]) {
        return [super gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch];
    }
    return YES;
}

#pragma mark - Setters

- (void)setGroupData:(SFGroup *)groupData {
    if (_groupData) {
        [_groupData removeObserver:self forKeyPath:@"expanded"];
    }
    if (groupData) {
        [groupData addObserver:self forKeyPath:@"expanded" options:NSKeyValueObservingOptionNew context:nil];
    }
    _groupData = groupData;
    [self updateChildren];
}

- (CGFloat)bodyViewAlpha {
    return self.bodyView.alpha;
}

#pragma mark - Getters

- (SFGroup *)groupData {
    return _groupData;
}

- (NSMutableArray<MOVABLE_VIEW *> *)rowViews {
    if (!_rowViews) {
        _rowViews = [NSMutableArray array];
    }
    return _rowViews;
}

- (CGFloat)bodyViewHeight {
    return CGRectGetHeight(self.bodyView.frame);
}

- (void)setBodyViewAlpha:(CGFloat)bodyViewAlpha {
    self.bodyView.alpha = bodyViewAlpha;
}

- (BOOL)bodyViewIsEmpty {
    return self.bodyView.subviews.count <= 0;
}

- (NSMutableArray<NSLayoutConstraint *> *)bodyConstraints {
    if (!_bodyConstraints) {
        _bodyConstraints = [NSMutableArray array];
    }
    return _bodyConstraints;
}

#pragma mark - Others 

- (void)dealloc {
    if (self.groupData) {
        [self.groupData removeObserver:self forKeyPath:@"expanded"];
    }
}

@end
